/**********************************************************************************************************************
 * @file    task_gnss.h
 * @author  Queclink Hubert.Lee
 * @date    2018-12-13
 * @brief   Queclink FreeRTOS Software Development Kit GNSS task.
 *
 * Copyright (C) 2018 Queclink Wireless Solutions (ShenZhen) Co., Ltd. All Rights Reserved.
 *
 * @attention
 *
 *********************************************************************************************************************/


#ifndef __TASK_GNSS_H
#define __TASK_GNSS_H

/* includes ---------------------------------------------------------------------------------------------------------*/
#include <time.h>
#include <FreeRTOS.h>
#include <stdbool.h>

#ifdef __cplusplus
extern "C" {
#endif

#ifndef _SSIZE_T
#define _SSIZE_T
typedef signed int ssize_t;
#endif /* _SSIZE_T */

#define TASK_NAME_GNSS                  "GNSS"                      /* GNSS 任务名称 */
#define GNSS_FW_VER_LEN                 (64)                        /* 固件版本信息最大长度 */
#define QLSDK_GNSS_COMM_BAUD            115200
#define QLSDK_GNSS_PWRON_DISCARD_TIME   5
#define QLSDK_GNSS_HDOP_THOLD           (7.0)
#define QLSDK_GNSS_MAX_SPEED            220
#define GNSS_STRERR_THOLD               100                         /* 标准差过滤 */

/* GNSS Types */
#define GNSS_GPS                        0x01                        /* GPS, America */
#define GNSS_GLONASS                    0x02                        /* GLONASS, Russian */
#define GNSS_GALILEO                    0x04                        /* Galileo, European Union */
#define GNSS_BEIDOU                     0x08                        /* BeiDou, China */

#define GNSS_MODE_DEFAULT_CONF          (GNSS_GPS|GNSS_BEIDOU|GNSS_GALILEO|GNSS_GLONASS)

#define GNSS_FIX_TIMEOUT_DEFAULT        200                         /* 默认的定位超时时间, 单位s */


/* 打印定位信息
 * prefix:前缀信息  fix:定位数据,struct fix_info*类型  sv_cnt:卫星数量 */
#define fix_info_printf(prefix, fix, sv_cnt,stderr) \
    do {    \
        gnss_trace(LOG_DEBUG"%s{is_fix:%s,posMode:%d,Sp:%5.1f,Lat:%9.6f,Log:%10.6f,Alt:%6.1f,Ag:%5.1f,Hdop:%4.1f,SV:%d,"\
                            "%02d-%02d-%02d %02d:%02d:%02d.%03d,stderr:%.1lf}\r\n",\
                        prefix, \
                        ((fix)->state==FIX_OK)? (((fix)->fixmode==FIXMODE_2D)? "2D fix":"3D fix"):"No fix",\
                        (fix)->posmode, (fix)->speed,\
                        (float)((fix)->latitude),(float)((fix)->longitude),\
                        (fix)->altitude,(fix)->angle,(fix)->hdop, (sv_cnt),\
                        (fix)->utc.tm_year-100,(fix)->utc.tm_mon+1,(fix)->utc.tm_mday,(fix)->utc.tm_hour,\
                        (fix)->utc.tm_min,(fix)->utc.tm_sec, (fix)->utc_ms,\
                        (stderr));  \
    } while(0)

#define FIX_INVALID     0               /* 定位信息无效 */
#define FIX_OK          1               /* 定位信息有效 */

#define POSMODE_N       0               /* posMode N, No position fix */
#define POSMODE_E       1               /* posMode E, Estimated, Dead reckoning fix */
#define POSMODE_A       2               /* posMode A, Autonomous GNSS fix */
#define POSMODE_D       3               /* posMode D, Differential GNSS fix */

#define FIXMODE_2D      0               /* 2D fix mode */
#define FIXMODE_3D      1               /* 3D fix mode */


/* NMEA 语句类型
 * 模块厂商特有的不包含在内, 如"TXT", "PMTK" 等 */
typedef enum {
    NMEA_TYPE_GGA,
    NMEA_TYPE_GLL,
    NMEA_TYPE_GSA,
    NMEA_TYPE_RMC,
    NMEA_TYPE_GSV,
    NMEA_TYPE_VTG,
} nmea_type;

/*
 * 定位信息,
 * UTC只在 FIX_OK 时有效.
 * 1.对于原始定位数据，无论当前是否定位所有
 *   成员的内容与模块输出数据同步更新;
 * 2.对于处理后的定位数据，仅FIX_OK时更新信息;
 */
struct fix_info {
    int             state;              /* 定位信息状态, FIX_INVALID or FIX_OK */
    int             posmode;            /* 定位模式, POSMODE_X */
    int             fixmode;            /* 定位模式, FIXMODE_XX */
    double          longitude;          /* 经度, 东经为正, 西经为负, 单位°*/
    double          latitude;           /* 纬度, 北纬为正, 南纬为负, 单位°*/
    float           altitude;           /* 海拔, 单位m, 一般范围-9999.9~99999.9m */
    float           speed;              /* 对地速度, 单位km/h */
    float           angle;              /* 地面航向(角度), 以正北为参考基准, 单位°*/
    float           hdop;               /* HDOP, 水平位置精度因子, 数值越小越准确 */
    struct tm       utc;                /* UTC时间(仅年月日时分秒有效) */
    short           utc_ms;             /* UTC时间ms信息 */
};

enum {
    GNSS_HOT_START,     /* 热启动 */
    GNSS_WARM_START,    /* 温启动 */
    GNSS_COLD_START,    /* 冷启动 */
};

/*
 * @brief  GNSS 任务可以休眠时间
 */
unsigned int qltask_gnss_canstop(void);

struct fix_point {
    double          longitude;          /* 经度, 东经为正, 西经为负, 单位°*/
    double          latitude;           /* 纬度, 北纬为正, 南纬为负, 单位°*/
};


enum {
    GNSS_ID_GPS     = 0,
    GNSS_ID_SBAS    = 1,
    GNSS_ID_GALILEO = 2,
    GNSS_ID_BEIDOU  = 3,
    GNSS_ID_IMES    = 4,
    GNSS_ID_QZSS    = 5,
    GNSS_ID_GLONASS = 6,
};

/* 惯导状态 */
typedef enum {                      /* 融合模式 */
    FUSION_INITIALIZATION,          /* 初始化模式 */
    FUSION_COMPUTATION,             /* 融合模式, 正在估算 */
    FUSION_SUSPENDED,               /* 挂起状态, 如 sensor 数据无效, 设备运输等 */
    FUSION_DISABLE,                 /* DR未使能 */
    FUSION_UNKNOWN,                 /* 未知 */
} fusion_mode;

struct satellite_t {
    uint8_t         gnss_id;            /* GNSS_ID_XXX */
    uint8_t         sv_id;              /* Satellite ID */
    uint8_t         elevation;          /* Elevation (range 0-90), unit:° */
    uint16_t        azimuth;            /* Azimuth, (range 0-359), unit:° */
    uint8_t         signal_strength;    /* Signal strength (C/N0, range 0-99), unit:dBHz */
};

/* satellites in view */
struct sat_info {
    unsigned int        count;              /* Number of satellites in view */
    struct satellite_t  sat[64];            /* 有效卫星信息 */
};

/*
 * @brief  更新 qltask_gnss_fix_get 的定位信息后执行的函数
 * @note   这是一个 weak 属性的函数,用户必要时可以重定义该函数
 * @param  new: [I], 最新的定位信息.
 * @param  stderr: [I], 位置误差的标准差
 * @retval none
 */
void qltask_gnss_fix_get_hook(const struct fix_info *new_, double stderr_);

/*
 * @brief  获取当前的卫星信息
 * @param  sat: [O], 当前的卫星信息.
 * @retval 0:成功, others:错误码
 */
int qltask_gnss_sat_get(struct sat_info *sat);

/* GNSS 信号等级 */
typedef enum {
    GNSS_SIGNAL_NO = 0,                 /* 无信号 */
    GNSS_SIGNAL_WEAK,                   /* 信号较弱 */
    GNSS_SIGNAL_GOOD,                   /* 信号良好 */
    GNSS_SIGNAL_STRONG,                 /* 信号较强 */
} gnss_signal;

/*
 * @brief  GNSS 日志通道打印接口
 * @note   为了简化日志管理, 用户可以直接使用该接口打印 GNSS 相关信息.
 */
#define gnss_trace(fmt, args...)    qltrace(fmt, ##args)

/*
 * @brief  获取GNSS固件版本信息
 * @note   字符串, 最大长度 GNSS_FW_VER_LEN;
 * @param  version,[O] 缓存版本信息, 仅返回 0 时有效
 *         size,[I] version 大小
 * @retval 0:成功, others:未获取有效版本信息
 */
int qltask_gnss_firmware_version(char *version, size_t size);

int32_t qltask_gnss_start(void);

#ifdef __cplusplus
}
#endif

#endif /* __TASK_GNSS_H */

